#pragma once

#include "DataCommon.h"
#include "DataConfigs.h"
#include "IRequestHander.h"

#include <map>
#include <list>
#include <set>

namespace data
{
	struct ManagerConfig
	{
		std::vector<MemorySlotConfig>	slotConfigs;
		std::vector<DataBlockConfig>	dataBlockConfigs;
	};

	class Manager
	{
	public:

		void Initialise(const ManagerConfig& config);
		void Shutdown();

		bool Step();

		void RegisterDataProcessor(IDataProcessor* pProcessor, cr::HashedString hsDataBlock);
		void UnregisterDataProcessor(IDataProcessor* pProcessor, cr::HashedString hsDataBlock);

		bool IsIdle() const
		{
			return ((currentRequest.state == Request::EState_Idle) && (requests.size() == 0));
		}

		void RequestData(const cr::HashedString& hsDataName, IRequestHandler* pHandler)
		{
			// Find data block
			DataBlock* pDataBlock = 0;
			for (std::vector<DataBlock>::iterator it = dataBlocks.begin(), end = dataBlocks.end(); it != end; ++it)
			{
				if (it->config.hsName == hsDataName)
				{
					pDataBlock = &*it;
					break;
				}
			}

			if (pDataBlock == 0)
			{
				CR_ERROR_STRING1("Error: Couldn't find requested datablock 0x%X", hsDataName);
			}

			// is data block already loaded?
			requests.push_back(Request());
			requests.back().state = Request::EState_Register_CheckingForLoadedData;
			requests.back().pHandler = pHandler;
			requests.back().hDataBlockHandle = DataBlockHandle(pDataBlock);
			requests.back().hFile = pf::FileIO::cInvalidFileHandle;
			requests.back().isReload = false;
		}

		void ReleaseData(cr::uint32_t hsDataName, IRequestHandler* pHandler)
		{
			// Find data block
			DataBlock* pDataBlock = 0;
			for (std::vector<DataBlock>::iterator it = dataBlocks.begin(), end = dataBlocks.end(); it != end; ++it)
			{
				if (it->config.hsName == hsDataName)
				{
					pDataBlock = &*it;
					break;
				}
			}

			if (pDataBlock == 0)
			{
				CR_ERROR_STRING1("Error: Couldn't find requested datablock 0x%X", hsDataName);
			}

			requests.push_back(Request());
			requests.back().hDataBlockHandle = pDataBlock;
			requests.back().state = Request::EState_Release_StartUnregistering;
			requests.back().pHandler = pHandler;
			requests.back().isReload = false;
		}

		static Manager& Get()
		{
			static Manager sManager;
			return sManager;
		}

	private:

		std::vector<Slot>											slots;
		std::vector<DataBlock>										dataBlocks;
		std::list<Request>											requests;
		std::map< cr::HashedString, std::set<IDataProcessor*> >		dataProcessors;

		Request														currentRequest;
		void*														pCurData;
		DataBlockHeader*											pCurHeader;
		std::set<IDataProcessor*>::iterator							itProcessor;
		std::set<IDataProcessor*>::iterator							itProcessorEnd;
		bool														enableHotload;
	};
}